package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"
	
	"go-ddns/iputil"
	"go-ddns/web"
)

// Configuration 保存应用配置信息
type Configuration struct {
	CloudflareAPIToken string   `json:"cloudflare_api_token"`
	ZoneID             string   `json:"zone_id,omitempty"` // 可选，如不提供则自动获取
	DomainName         string   `json:"domain_name,omitempty"` // 用于自动获取zone_id
	DomainNames        []string `json:"domain_names"`
	CheckInterval      int      `json:"check_interval"`
	IPVersion          string   `json:"ip_version"`
	IPv4Services       []string `json:"ipv4_services,omitempty"` // IPv4检测服务列表
	IPv6Services       []string `json:"ipv6_services,omitempty"` // IPv6检测服务列表
}

// DDNSLogEntry 表示一条DDNS更新日志
type DDNSLogEntry struct {
	Timestamp    time.Time `json:"timestamp"`     // 日志时间
	IPVersion    string    `json:"ip_version"`    // IP版本（IPv4/IPv6）
	CurrentIP    string    `json:"current_ip"`    // 当前检测到的IP
	PreviousIP   string    `json:"previous_ip"`   // 上一次的IP
	Changed      bool      `json:"changed"`       // IP是否变化
	Domains      []string  `json:"domains"`       // 更新的域名列表
	Success      bool      `json:"success"`       // 更新是否成功
	ErrorMessage string    `json:"error_message"` // 错误信息（如果有）
}

// DNSRecord 表示来自Cloudflare API的DNS记录
type DNSRecord struct {
	ID      string `json:"id"`
	Name    string `json:"name"`
	Type    string `json:"type"`
	Content string `json:"content"`
	TTL     int    `json:"ttl"`
	Proxied bool   `json:"proxied"`
}

// DNSRecordsResponse 表示Cloudflare API的响应
type DNSRecordsResponse struct {
	Success bool        `json:"success"`
	Errors  []APIError  `json:"errors"`
	Result  []DNSRecord `json:"result"`
}

// APIError 表示Cloudflare API的错误信息
type APIError struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

// UpdateResponse 表示更新DNS记录后的响应
type UpdateResponse struct {
	Success bool       `json:"success"`
	Errors  []APIError `json:"errors"`
}

var (
	currentConfig *Configuration
	configMutex   sync.RWMutex
	ddnsTicker    *time.Ticker
	stopChan      chan struct{}
	
	// 日志相关变量
	ddnsLogs               []DDNSLogEntry // 存储DDNS日志
	ddnsLogsMutex          sync.RWMutex   // 保护日志访问的互斥锁
	maxLogEntries          = 100          // 内存中保存的最大日志条数
	maxPersistentLogEntries = 1000        // 持久化存储的最大日志条数
	logFilePath            = "logs/ddns_logs.json" // 日志文件路径
	logSaveInterval        = 1 * time.Hour // 定期保存日志的间隔
)

// loadConfig 从JSON文件加载配置信息
func loadConfig(filename string) (*Configuration, error) {
	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	config := &Configuration{}
	decoder := json.NewDecoder(file)
	err = decoder.Decode(config)
	if err != nil {
		return nil, err
	}

	// 如果未指定检查间隔，则设置默认值
	if config.CheckInterval <= 0 {
		config.CheckInterval = 300 // 默认5分钟
	}

	return config, nil
}

// 添加新日志
func addDDNSLog(entry DDNSLogEntry) {
	ddnsLogsMutex.Lock()
	defer ddnsLogsMutex.Unlock()
	
	// 添加新日志到开头
	ddnsLogs = append([]DDNSLogEntry{entry}, ddnsLogs...)
	
	// 如果日志超出限制，截断
	if len(ddnsLogs) > maxLogEntries {
		ddnsLogs = ddnsLogs[:maxLogEntries]
	}
}

// 获取最近的日志
func getDDNSLogs(count int) []DDNSLogEntry {
	ddnsLogsMutex.RLock()
	defer ddnsLogsMutex.RUnlock()
	
	if count <= 0 || count > len(ddnsLogs) {
		count = len(ddnsLogs)
	}
	
	result := make([]DDNSLogEntry, count)
	copy(result, ddnsLogs[:count])
	return result
}

// 保存日志到文件
func saveLogs() error {
	ddnsLogsMutex.RLock()
	defer ddnsLogsMutex.RUnlock()
	
	// 创建目录
	dir := filepath.Dir(logFilePath)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return err
	}
	
	// 写入文件
	file, err := os.Create(logFilePath)
	if err != nil {
		return err
	}
	defer file.Close()
	
	// 只保存最近的日志
	logsToSave := ddnsLogs
	if len(ddnsLogs) > maxPersistentLogEntries {
		logsToSave = ddnsLogs[:maxPersistentLogEntries]
	}
	
	encoder := json.NewEncoder(file)
	encoder.SetIndent("", "  ")
	return encoder.Encode(logsToSave)
}

// 从文件加载日志
func loadLogs() error {
	file, err := os.Open(logFilePath)
	if os.IsNotExist(err) {
		// 文件不存在，使用空日志
		ddnsLogs = []DDNSLogEntry{}
		return nil
	}
	if err != nil {
		return err
	}
	defer file.Close()
	
	// 先加载所有日志
	var allLogs []DDNSLogEntry
	decoder := json.NewDecoder(file)
	err = decoder.Decode(&allLogs)
	if err != nil {
		return err
	}
	
	// 限制加载到内存中的日志数量
	if len(allLogs) > maxLogEntries {
		ddnsLogs = allLogs[:maxLogEntries]
	} else {
		ddnsLogs = allLogs
	}
	
	return nil
}

// 启动定期保存日志的goroutine
func startLogSaver() {
	ticker := time.NewTicker(logSaveInterval)
	go func() {
		for {
			<-ticker.C
			if err := saveLogs(); err != nil {
				log.Printf("自动保存日志失败: %v", err)
			} else {
				log.Printf("已自动保存日志到 %s", logFilePath)
			}
		}
	}()
}

// getZoneID 自动获取域名的ZoneID
func getZoneID(apiToken, domainName string) (string, error) {
	url := "https://api.cloudflare.com/client/v4/zones"
	
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return "", err
	}
	
	req.Header.Set("Authorization", "Bearer "+apiToken)
	req.Header.Set("Content-Type", "application/json")
	
	client := &http.Client{
		Timeout: 30 * time.Second,
	}
	resp, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	
	// 解析响应
	var result struct {
		Success bool `json:"success"`
		Errors  []struct {
			Message string `json:"message"`
		} `json:"errors"`
		Result []struct {
			ID   string `json:"id"`
			Name string `json:"name"`
		} `json:"result"`
	}
	
	err = json.Unmarshal(body, &result)
	if err != nil {
		return "", err
	}
	
	if !result.Success {
		errMsg := "API错误"
		if len(result.Errors) > 0 {
			errMsg = fmt.Sprintf("API错误: %v", result.Errors[0].Message)
		}
		return "", fmt.Errorf(errMsg)
	}
	
	// 查找匹配的域名
	for _, zone := range result.Result {
		// 检查domainName是否是zone.Name或者是其子域名
		if zone.Name == domainName || strings.HasSuffix(domainName, "."+zone.Name) {
			return zone.ID, nil
		}
	}
	
	return "", fmt.Errorf("找不到域名 %s 的区域ID", domainName)
}

// validateConfig 验证配置信息
func validateConfig(config *Configuration) error {
	if config.CloudflareAPIToken == "" {
		return fmt.Errorf("必须提供cloudflare_api_token")
	}
	
	// 如果未提供ZoneID，则尝试自动获取
	if config.ZoneID == "" {
		// 需要至少有一个域名用于获取ZoneID
		if len(config.DomainNames) == 0 && config.DomainName == "" {
			return fmt.Errorf("未提供zone_id时，必须提供domain_name或domain_names")
		}
		
		// 确定要用于获取ZoneID的域名
		domainForZoneID := config.DomainName
		if domainForZoneID == "" {
			domainForZoneID = config.DomainNames[0]
		}
		
		log.Printf("未提供zone_id，尝试使用域名 %s 自动获取...", domainForZoneID)
		var err error
		config.ZoneID, err = getZoneID(config.CloudflareAPIToken, domainForZoneID)
		if err != nil {
			return fmt.Errorf("自动获取zone_id失败: %v", err)
		}
		log.Printf("自动获取zone_id成功: %s", config.ZoneID)
	}
	
	// 确保至少有一个域名需要更新
	if len(config.DomainNames) == 0 {
		// 如果只有DomainName而没有DomainNames，则将DomainName添加到DomainNames
		if config.DomainName != "" {
			config.DomainNames = []string{config.DomainName}
		} else {
			return fmt.Errorf("至少需要提供一个域名")
		}
	}
	
	// 验证并设置默认的IP版本
	if config.IPVersion == "" {
		config.IPVersion = "ipv4" // 默认使用IPv4
	} else if config.IPVersion != "ipv4" && config.IPVersion != "ipv6" {
		return fmt.Errorf("ip_version 必须是 'ipv4' 或 'ipv6'")
	}
	
	// 设置自定义IP检测服务
	if len(config.IPv4Services) > 0 {
		iputil.SetIPv4Services(config.IPv4Services)
	}
	if len(config.IPv6Services) > 0 {
		iputil.SetIPv6Services(config.IPv6Services)
	}
	
	return nil
}

// getDNSRecords 从Cloudflare获取DNS记录
func getDNSRecords(config *Configuration, domainName, recordType string) ([]DNSRecord, error) {
	url := fmt.Sprintf("https://api.cloudflare.com/client/v4/zones/%s/dns_records?type=%s&name=%s", config.ZoneID, recordType, domainName)
	
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}
	
	req.Header.Set("Authorization", "Bearer "+config.CloudflareAPIToken)
	req.Header.Set("Content-Type", "application/json")
	
	client := &http.Client{
		Timeout: 30 * time.Second,
	}
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	
	var recordsResponse DNSRecordsResponse
	err = json.Unmarshal(body, &recordsResponse)
	if err != nil {
		return nil, err
	}
	
	if !recordsResponse.Success {
		errMsg := "API错误"
		if len(recordsResponse.Errors) > 0 {
			errMsg = fmt.Sprintf("API错误: %v", recordsResponse.Errors[0].Message)
		}
		return nil, fmt.Errorf(errMsg)
	}
	
	return recordsResponse.Result, nil
}

// updateDNSRecord 更新Cloudflare上的DNS记录
func updateDNSRecord(config *Configuration, recordID, domainName, ip, recordType string) error {
	url := fmt.Sprintf("https://api.cloudflare.com/client/v4/zones/%s/dns_records/%s", config.ZoneID, recordID)
	
	data := map[string]interface{}{
		"type":    recordType,
		"name":    domainName,
		"content": ip,
		"ttl":     1, // 自动TTL
		"proxied": false,
	}
	
	jsonData, err := json.Marshal(data)
	if err != nil {
		return err
	}
	
	req, err := http.NewRequest("PUT", url, strings.NewReader(string(jsonData)))
	if err != nil {
		return err
	}
	
	req.Header.Set("Authorization", "Bearer "+config.CloudflareAPIToken)
	req.Header.Set("Content-Type", "application/json")
	
	client := &http.Client{
		Timeout: 30 * time.Second,
	}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	
	var updateResponse UpdateResponse
	err = json.Unmarshal(body, &updateResponse)
	if err != nil {
		return err
	}
	
	if !updateResponse.Success {
		errMsg := "更新DNS记录失败"
		if len(updateResponse.Errors) > 0 {
			errMsg = fmt.Sprintf("更新DNS记录失败: %v", updateResponse.Errors[0].Message)
		}
		return fmt.Errorf(errMsg)
	}
	
	return nil
}

// createDNSRecord 在Cloudflare上创建新的DNS记录
func createDNSRecord(config *Configuration, domainName, ip, recordType string) error {
	url := fmt.Sprintf("https://api.cloudflare.com/client/v4/zones/%s/dns_records", config.ZoneID)
	
	data := map[string]interface{}{
		"type":    recordType,
		"name":    domainName,
		"content": ip,
		"ttl":     1, // 自动TTL
		"proxied": false,
	}
	
	jsonData, err := json.Marshal(data)
	if err != nil {
		return err
	}
	
	req, err := http.NewRequest("POST", url, strings.NewReader(string(jsonData)))
	if err != nil {
		return err
	}
	
	req.Header.Set("Authorization", "Bearer "+config.CloudflareAPIToken)
	req.Header.Set("Content-Type", "application/json")
	
	client := &http.Client{
		Timeout: 30 * time.Second,
	}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	
	var updateResponse UpdateResponse
	err = json.Unmarshal(body, &updateResponse)
	if err != nil {
		return err
	}
	
	if !updateResponse.Success {
		errMsg := "创建DNS记录失败"
		if len(updateResponse.Errors) > 0 {
			errMsg = fmt.Sprintf("创建DNS记录失败: %v", updateResponse.Errors[0].Message)
		}
		return fmt.Errorf(errMsg)
	}
	
	return nil
}

// updateDDNS 如果IP地址有变化，则更新DNS记录
func updateDDNS(config *Configuration) {
	log.Printf("尝试获取当前%s地址...", strings.ToUpper(config.IPVersion))
	
	// 创建日志条目
	logEntry := DDNSLogEntry{
		Timestamp: time.Now(),
		IPVersion: config.IPVersion,
		Domains:   config.DomainNames,
		Success:   true, // 默认成功，如有错误再修改
	}
	
	currentIP, err := iputil.GetPublicIP(config.IPVersion)
	if err != nil {
		log.Printf("获取当前%s时出错: %v", strings.ToUpper(config.IPVersion), err)
		if config.IPVersion == "ipv6" {
			log.Printf("注意: 如果您的网络不支持IPv6，请将配置中的ip_version更改为'ipv4'")
			log.Printf("您也可以尝试运行以下命令检查是否支持IPv6: 'curl http://v6.66666.host:66/ip'")
		}
		
		// 记录错误
		logEntry.Success = false
		logEntry.ErrorMessage = fmt.Sprintf("获取IP失败: %v", err)
		addDDNSLog(logEntry)
		return
	}
	
	logEntry.CurrentIP = currentIP
	log.Printf("成功获取当前%s: %s", strings.ToUpper(config.IPVersion), currentIP)
	
	// 根据IP版本决定DNS记录类型
	recordType := "A"
	if config.IPVersion == "ipv6" {
		recordType = "AAAA"
	}
	
	for _, domainName := range config.DomainNames {
		// 传递记录类型给获取DNS记录的函数
		records, err := getDNSRecords(config, domainName, recordType)
		if err != nil {
			log.Printf("获取%s的%s记录时出错: %v", domainName, recordType, err)
			logEntry.Success = false
			logEntry.ErrorMessage = fmt.Sprintf("获取%s的DNS记录失败: %v", domainName, err)
			continue
		}
		
		if len(records) == 0 {
			log.Printf("未找到%s的%s记录，创建新记录", domainName, recordType)
			err = createDNSRecord(config, domainName, currentIP, recordType)
			if err != nil {
				log.Printf("为%s创建%s记录时出错: %v", domainName, recordType, err)
				logEntry.Success = false
				logEntry.ErrorMessage = fmt.Sprintf("创建DNS记录失败: %v", err)
			} else {
				log.Printf("为%s创建了新的%s记录，IP为%s", domainName, recordType, currentIP)
			}
		} else {
			record := records[0]
			logEntry.PreviousIP = record.Content
			
			if record.Content != currentIP {
				logEntry.Changed = true
				log.Printf("%s的%s已改变: %s -> %s", domainName, strings.ToUpper(config.IPVersion), record.Content, currentIP)
				err = updateDNSRecord(config, record.ID, domainName, currentIP, recordType)
				if err != nil {
					log.Printf("更新%s的%s记录时出错: %v", domainName, recordType, err)
					logEntry.Success = false
					logEntry.ErrorMessage = fmt.Sprintf("更新DNS记录失败: %v", err)
				} else {
					log.Printf("已将%s的%s记录更新为%s", domainName, recordType, currentIP)
				}
			} else {
				logEntry.Changed = false
				log.Printf("%s的%s未变化 (%s)", domainName, strings.ToUpper(config.IPVersion), currentIP)
			}
		}
	}
	
	// 添加日志
	addDDNSLog(logEntry)
}

// 开始DDNS更新服务
func startDDNSService(config *Configuration) {
	configMutex.Lock()
	currentConfig = config
	
	// 停止正在运行的ticker
	if ddnsTicker != nil {
		ddnsTicker.Stop()
	}
	if stopChan != nil {
		close(stopChan)
	}
	
	// 创建新的停止通道
	stopChan = make(chan struct{})
	configMutex.Unlock()
	
	// 验证配置
	err := validateConfig(config)
	if err != nil {
		log.Fatalf("配置无效: %v", err)
	}
	
	log.Printf("DDNS更新程序已启动，域名: %v", config.DomainNames)
	log.Printf("检查间隔: %d 秒", config.CheckInterval)
	
	// 立即运行一次
	updateDDNS(config)
	
	// 设置定期检查
	configMutex.Lock()
	ddnsTicker = time.NewTicker(time.Duration(config.CheckInterval) * time.Second)
	tickerChan := ddnsTicker.C
	localStopChan := stopChan
	configMutex.Unlock()
	
	go func() {
		for {
			select {
			case <-tickerChan:
				configMutex.RLock()
				cfg := currentConfig
				configMutex.RUnlock()
				updateDDNS(cfg)
			case <-localStopChan:
				return
			}
		}
	}()
}

// 重新加载配置并重启服务
func reloadConfig() {
	log.Println("正在重新加载配置...")
	
	// 获取当前配置文件路径
	configMutex.RLock()
	configFile := "config/config.json" // 默认值
	configMutex.RUnlock()
	
	// 从Web配置获取最新数据
	webCfg := web.GetWebConfig()
	if webCfg != nil {
		config := &Configuration{
			CloudflareAPIToken: webCfg.CloudflareAPIToken,
			DomainNames:        webCfg.DomainNames,
			CheckInterval:      webCfg.CheckInterval,
			IPVersion:          webCfg.IPVersion,
			IPv4Services:       webCfg.IPv4Services,
			IPv6Services:       webCfg.IPv6Services,
		}
		
		log.Println("从Web界面加载了新配置")
		startDDNSService(config)
		return
	}
	
	// 如果Web配置不可用，从文件加载
	config, err := loadConfig(configFile)
	if err != nil {
		log.Printf("重新加载配置失败: %v", err)
		return
	}
	
	startDDNSService(config)
}

func main() {
	webEnabled := flag.Bool("web", true, "启用Web配置界面")
	webPort := flag.Int("port", 8080, "Web配置界面端口")
	configFile := flag.String("config", "config/config.json", "配置文件路径")
	fileServerConfig := flag.String("fileserver-config", "config/fileserver.json", "文件服务器配置文件路径")
	flag.Parse()
	
	// 设置日志
	log.SetOutput(os.Stdout)
	log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds)
	
	// 加载日志
	if err := loadLogs(); err != nil {
		log.Printf("加载日志失败: %v，将使用空日志", err)
	} else {
		log.Printf("已加载 %d 条历史日志记录", len(ddnsLogs))
	}
	
	// 启动定期保存日志
	startLogSaver()
	
	// 加载DDNS配置文件
	config, err := loadConfig(*configFile)
	if err != nil {
		// 尝试加载备用路径
		if *configFile == "config/config.json" && strings.Contains(err.Error(), "no such file or directory") {
			// 尝试从当前目录加载
			altConfigFile := "config.json"
			log.Printf("未能从 %s 加载配置，尝试从 %s 加载...", *configFile, altConfigFile)
			config, err = loadConfig(altConfigFile)
			if err != nil {
				if *webEnabled {
					// 如果没有配置文件但启用了Web界面，使用默认配置
					log.Printf("未能加载配置文件，使用默认配置并启动Web配置界面...")
					config = &Configuration{
						DomainNames:   []string{""},
						CheckInterval: 300,
						IPVersion:     "ipv4",
					}
				} else {
					log.Fatalf("加载配置时出错: %v", err)
				}
			}
		} else if *webEnabled {
			// 如果启用了Web界面，使用默认配置
			log.Printf("加载配置失败: %v，使用默认配置并启动Web配置界面...", err)
			config = &Configuration{
				DomainNames:   []string{""},
				CheckInterval: 300,
				IPVersion:     "ipv4",
			}
		} else {
			log.Fatalf("加载配置时出错: %v", err)
		}
	}
	
	// 启动DDNS服务
	startDDNSService(config)
	
	// 加载并启动文件服务器
	_, err = web.LoadFileServerConfig(*fileServerConfig)
	if err != nil {
		log.Printf("加载文件服务器配置失败: %v，将使用默认配置", err)
	}
	
	// 启动文件服务器（如果已配置启用）
	if err := web.StartFileServer(); err != nil {
		log.Printf("启动文件服务器时出错: %v", err)
	}
	
	// 如果启用Web界面，启动Web服务器
	if *webEnabled {
		log.Printf("启动Web配置界面，端口: %d", *webPort)
		
		// 使用jsonFile变量保存配置路径
		jsonFile := *configFile
		
		// 设置日志回调函数
		web.SetGetLogsCallback(func(count int) []web.DDNSLogEntry {
			logs := getDDNSLogs(count)
			// 将内部日志类型转换为web包中的日志类型
			webLogs := make([]web.DDNSLogEntry, len(logs))
			for i, log := range logs {
				webLogs[i] = web.DDNSLogEntry{
					Timestamp:    log.Timestamp,
					IPVersion:    log.IPVersion,
					CurrentIP:    log.CurrentIP,
					PreviousIP:   log.PreviousIP,
					Changed:      log.Changed,
					Domains:      log.Domains,
					Success:      log.Success,
					ErrorMessage: log.ErrorMessage,
				}
			}
			return webLogs
		})
		
		// 启动Web服务器，异步执行
		go func() {
			err := web.StartWebServer(jsonFile, reloadConfig)
			if err != nil {
				log.Printf("Web服务器启动失败: %v", err)
			}
		}()
	}
	
	// 阻止主goroutine退出
	select {}
} 